#include <defs.h>
#include <io.h>
#include <bwio.h>
#include <user.h>
#include <timer.h>
#include <vic.h>
#include <uart.h>
#include <ts7200.h>
#include <pq.h>
#include <syscalls.h>
#include <kernel.h>
#include <isr.h>
#include <debug.h>

void Initialize( sched *s ) {  
  // install software interrupt handler
  int *swi_handler = (int*)( 0x28 );
  *swi_handler = (int)( &KernelEnter ) + CODE_BASE;

  // install abort handlers
  int *prefetch_abort_handler = (int*)( 0x2c );
  *prefetch_abort_handler = (int)( &AbortEnter ) + CODE_BASE;
  int *data_abort_handler = (int*)( 0x30 );
  *data_abort_handler = (int)( &AbortEnter ) + CODE_BASE;

  // install hardware interrupt handler
  int *irq_handler = (int*)( 0x38 );
  *irq_handler = (int)( &KernelEnter ) + CODE_BASE;

  // install default handler
  vic *const vic2 = (vic*)( VIC2_BASE );
  vic2->def_vect_addr = (int)( &DefaultHandler ) + CODE_BASE;

  // set up data structures
  int i;
  for (i = 0; i <= MAX_PRI; i++) {
    PqInit( &(s->queues[i]) );
  }
  for (i = 0; i < EVENT_MAX; i++) {
    s->waiters[i] = -1;
  }
  s->next_task_id = 0;

  // create first user task
  SysCreate( s, NULL, 2, &UserInit );
}

td* Schedule( sched *s ) {
  int i;
  for (i = MAX_PRI; i >= 0; i--) {
    if ( !PqEmpty( &(s->queues[i]) ) ) break;
  }

  if ( i == -1 ) return NULL;
  td *next_task = PqPopFront( &(s->queues[i]) );
  next_task->state = STATE_ACTIVE;
  return next_task;
}

void Activate( sched *s, td *task, req *request ) {
  KernelExit( task, request );
}

void Handle( sched *s, td *task, req *request ) { 
  switch ( request->call ) {
    case SYSCALL_CREATE:
		  task->ret = SysCreate( s, task, request->a0, (void (*)())request->a1 );
		  break;
    case SYSCALL_MY_TID:
		  task->ret = SysMyTid( s, task );
		  break;
    case SYSCALL_MY_PARENT_TID:
		  task->ret = SysMyParentTid( s, task );
		  break;
    case SYSCALL_PASS:
		  SysPass( s, task );
		  break;
    case SYSCALL_EXIT:
		  SysExit( s, task );
		  break;
    case SYSCALL_FINGER_OF_DEATH:
      SysFingerOfDeath( s, task );
      break;
    case SYSCALL_SEND:
      task->ret = SysSend( s, task,
          request->a0,
          (char*)request->a1, request->a2,
          (char*)request->a3, request->a4 );
      break;
    case SYSCALL_RECEIVE:
      task->ret = SysReceive( s, task,
          (int*)request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_REPLY:
      task->ret = SysReply( s, task,
          request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_AWAIT_EVENT:
      task->ret = SysAwaitEvent( s, task,
          request->a0, (char*)request->a1, request->a2 );
      break;
    case SYSCALL_PARAM_TEST:
      task->ret = SysParamTest( s, task,
          request->a0, request->a1, request->a2, request->a3, request->a4 );
      break;
    case HARDWARE_INTERRUPT:
    {
      vic *const vic2 = (vic*)( VIC2_BASE );
      void (*InterruptServiceRoutine)(sched*, td*) =
          (void (*)(sched*, td*))vic2->vect_addr;
      InterruptServiceRoutine( s, task );
      break;
    }
    default:
	    Printf(COM2, "Warning: Unsupported SYSCALL issued: %d\r\n", request->call);
	  break;
  }
  
  if ( task->state == STATE_READY || task->state == STATE_ACTIVE ) {
    // reinsert at back of ready queue
    task->state = STATE_READY;
    PqPushBack( &(s->queues[task->pri]), task );
  }
}

int main() {
  sched scheduler;
  Initialize( &scheduler );
  td *next_task;
  req request;
  for ( ; ; ) {    
    next_task = Schedule( &scheduler );
    if ( next_task == NULL ) break;
    Activate( &scheduler, next_task, &request );
    Handle( &scheduler, next_task, &request );
  }
  return 0;
}
